﻿namespace Summoner
{
    using UnityEngine;
    using System;
    using System.Collections.Generic;

    public class AssetManager : IDisposable
    {
        private static AssetManager _instance = null;
        private static Dictionary<string, AtlasObject> _name2atlas = new Dictionary<string, AtlasObject>();
        private static GameObjectPoolCtr _poolCtrl = new GameObjectPoolCtr();

        public AssetManager() { _instance = this; }

        /// <summary>
        /// Initilaize the Asset manager
        /// </summary>
        /// <param name="callback"></param>
        public static void Init(Action callback)
        {
            AssetObtainer.instance.Init(() => { if (callback != null) { callback(); } });
        }

        /// <summary>
        /// load gameobject async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadGameObjectAsync(string name, Action<GameObject> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
            {
                GameObject go = (GameObject)obj;
                if (go != null) { if (callback != null) { callback(go); } }
                else { ULogger.LogError(string.Format("Load GameObject {0} failed !", name)); }
            });
        }

        /// <summary>
        /// load gameobject with parameters async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parameter"></param>
        /// <param name="callback"></param>
        public static void LoadGameObjectWithParameterAsync(string name, object parameter, Action<GameObject, object> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
            {
                GameObject go = (GameObject)obj;
                if (go != null) { if (callback != null) { callback(go, parameter); } }
                else { ULogger.LogError(string.Format("Load GameObject {0} failed !", name)); }
            }, parameter);
        }

        /// <summary>
        /// load gameobject with pooled async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadGameObjectPooledAsync(string name, Action<GameObject> callback)
        {
            AssetObtainer.instance.LoadAssetPooledAsync(name, (obj) =>
            {
                GameObject go = (GameObject)obj;
                if (go != null) { if (callback != null) { callback(go); } }
                else { ULogger.LogError(string.Format("Load GameObject {0} failed !", name)); }
            });
        }

        /// <summary>
        /// load gameobject pooled with parameters async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="parameter"></param>
        /// <param name="callback"></param>
        public static void LoadGameObjectPooledWithParameterAsync(string name, object parameter, Action<GameObject, object> callback)
        {
            AssetObtainer.instance.LoadAssetPooledAsync(name, (obj) =>
            {
                GameObject go = (GameObject)obj;
                if (go != null) { if (callback != null) { callback(go, parameter); } }
                else { ULogger.LogError(string.Format("Load GameObject {0} failed !", name)); }
            }, parameter);
        }

        /// <summary>
        /// load gameobject sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static GameObject LoadGameObjectSync(string name)
        {
            return (GameObject)AssetObtainer.instance.LoadAssetSync(name);
        }

        /// <summary>
        /// load gameobject pooled sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static GameObject LoadGameObjectPooledSync(string name)
        {
            return (GameObject)AssetObtainer.instance.LoadAssetPooledSync(name);
        }

        /// <summary>
        /// load sprite async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        // public static void LoadSpriteAsync(string name, Action<Sprite> callback)
        // {
        //     AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
        //     {
        //         if (obj != null)
        //         {
        //             GameObject go = (GameObject)obj;
        //             Sprite temp = go.GetComponent<SpriteRenderer>().sprite;
        //             Sprite sprite = Sprite.Create(temp.texture, temp.rect, temp.pivot, temp.pixelsPerUnit, 1, SpriteMeshType.FullRect, temp.border);
        //             UnityEngine.Object.Destroy(go);
        //             if (callback != null) { callback(sprite); }
        //         }
        //         else { ULogger.LogError(string.Format("Load Sprite {0} failed !", name)); if (callback != null) { callback(null); } }
        //     });
        // }

        /// <summary>
        /// load sprite async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        // public static void LoadSpriteAsync(string atlasName, string name, Action<Sprite> callback)
        // {
        //     if (!_name2atlas.ContainsKey(atlasName))
        //     {
        //         AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
        //         {
        //             if (obj != null)
        //             {
        //                 ScriptableObject scriptable = (ScriptableObject)obj;
        //                 _name2atlas[atlasName] = (AtlasObject)scriptable;
        //                 if (callback != null) { callback(_name2atlas[atlasName].GetSprite(name)); }
        //             }
        //             else { ULogger.LogError(string.Format("Load Sprite {0} failed !", name)); if (callback != null) { callback(null); } }
        //         });
        //     }
        //     else
        //     {
        //         if (callback != null) { callback(_name2atlas[atlasName].GetSprite(name)); }
        //     }
        // }

        /// <summary>
        /// fetch sprite async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void FetchSpriteAsync(string atlasName, string name, Action<Sprite> callback)
        {
            if (!_name2atlas.ContainsKey(atlasName))
            {
                AssetObtainer.instance.LoadAssetAsync(atlasName, (obj) =>
                {
                    if (obj != null)
                    {
                        Texture2D atlasTexture = (Texture2D)obj;
                        if(atlasTexture != null)
                        {
                            AssetObtainer.instance.LoadAssetAsync(atlasName + "_cfg", (o) =>
                            {
                                if (!string.IsNullOrEmpty(o.ToString()))
                                {
                                    List<SpriteInfo> spriteList = JsonSerializer.UnpackJson<List<SpriteInfo>>(o.ToString());
                                    AtlasObject atlasObj = ScriptableObject.CreateInstance<AtlasObject>();
                                    for (int i = 0; i < spriteList.Count; i++)
                                    {
                                        SpriteInfo info = spriteList[i];
                                        Sprite sprite = Sprite.Create(atlasTexture, info.rect, info.pivot, info.pixelsPerUnit, 0, SpriteMeshType.Tight, info.border);
                                        sprite.name = info.name;
                                        atlasObj.tileSprites.Add(sprite);
                                    }
                                    _name2atlas[atlasName] = atlasObj;
                                    if (callback != null) { callback(_name2atlas[atlasName].GetSprite(name)); }
                                }
                                else
                                {
                                    ULogger.LogError(string.Format("Load Atlas Config {0} failed !", atlasName + "_cfg"));
                                    if (callback != null) { callback(null); }
                                }
                            });
                        }
                        else
                        {
                            ULogger.LogError(string.Format("Load Atlas Config {0} failed !", atlasName + "_cfg"));
                            if (callback != null) { callback(null); }
                        }
                    }
                    else
                    {
                        ULogger.LogError(string.Format("Load Sprite {0} failed !", atlasName));
                        if (callback != null) { callback(null); }
                    }
                });
            }
            else
            {
                if (callback != null) { callback(_name2atlas[atlasName].GetSprite(name)); }
            }
        }

        /// <summary>
        /// load sprite sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        // public static Sprite LoadSpriteSync(string name)
        // {
        //     GameObject go = ((GameObject)AssetObtainer.instance.LoadAssetSync(name));
        //     if (go == null)
        //     {
        //         ULogger.LogError(string.Format("Load Sprite {0} failed !", name));
        //         return null;
        //     }
        //     else
        //     {
        //         Sprite temp = go.GetComponent<SpriteRenderer>().sprite;
        //         Sprite sprite = Sprite.Create(temp.texture, temp.rect, temp.pivot, temp.pixelsPerUnit, 1, SpriteMeshType.FullRect, temp.border);
        //         UnityEngine.Object.Destroy(go);
        //         return sprite;
        //     }
        // }

        /// <summary>
        /// load sprite sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        // public static Sprite LoadSpriteSync(string atlasName, string name)
        // {
        //     if(!_name2atlas.ContainsKey(atlasName))
        //     {
        //         ScriptableObject scriptable = (ScriptableObject)AssetObtainer.instance.LoadAssetSync(atlasName);
        //         if (scriptable == null)
        //         {
        //             ULogger.LogError(string.Format("Load Sprite {0} failed !", name));
        //             return null;
        //         }
        //         else
        //         {
        //             _name2atlas[atlasName] = (AtlasObject)scriptable;
        //         }
        //     }
        //     return _name2atlas[atlasName].GetSprite(name);
        // }

        /// <summary>
        /// fetch sprite sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Sprite FetchSpriteSync(string atlasName, string name)
        {
            if (!_name2atlas.ContainsKey(atlasName))
            {
                Texture2D atlasTexture = (Texture2D)AssetObtainer.instance.LoadAssetSync(atlasName);
                TextAsset texta = (TextAsset)AssetObtainer.instance.LoadAssetSync(atlasName + "_cfg");
                List<SpriteInfo> spriteList = JsonSerializer.UnpackJson<List<SpriteInfo>>(texta.text);
                AtlasObject atlasObj = ScriptableObject.CreateInstance<AtlasObject>();
                for (int i = 0; i < spriteList.Count; i++)
                {
                    SpriteInfo info = spriteList[i];
                    Sprite sprite = Sprite.Create(atlasTexture, info.rect, info.pivot, info.pixelsPerUnit, 0, SpriteMeshType.Tight, info.border);
                    sprite.name = info.name;
                    atlasObj.tileSprites.Add(sprite);
                }
                _name2atlas[atlasName] = atlasObj;
            }
            return _name2atlas[atlasName].GetSprite(name);
        }

        /// <summary>
        /// delete specifical atlas
        /// </summary>
        /// <param name="atlasName"></param>
        public static void DeleteSpriteAtlas(string atlasName)
        {
            if (_name2atlas.ContainsKey(atlasName))
            {
                UnityEngine.Object.Destroy(_name2atlas[atlasName]);
                _name2atlas.Remove(atlasName);
            }
        }

        /// <summary>
        /// load config with ext name
        /// </summary>
        /// <param name="name"></param>
        /// <param name="ext"></param>
        /// <param name="callback"></param>
        public static void LoadConfig(string name, Action<string> callback, string ext = ".json")
        {
            AssetObtainer.instance.LoadConfig(name, callback, ext);
        }

        /// <summary>
        /// load config content from assetbundle async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadConfigAsync(string name, Action<string> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
            {
                if (obj != null)
                {
                    if (callback != null) { callback(((TextAsset)obj).text); }
                }
                else
                {
                    ULogger.LogError(string.Format("Load Texture {0} failed !", name));
                }
            });
        }

        /// <summary>
        /// load config content from assetbundle sync
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public static string LoadConfigSync(string name)
        {
            return ((TextAsset)AssetObtainer.instance.LoadAssetSync(name)).text;
        }

        /// <summary>
        /// load bytes from assetbundle async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadBytesAsync(string name, Action<byte[]> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj) =>
            {
                if (obj != null)
                {
                    if (callback != null) { callback(((TextAsset)obj).bytes); }
                }
                else
                {
                    ULogger.LogError(string.Format("Load Texture {0} failed !", name));
                }
            });
        }

        /// <summary>
        /// load bytes from assetbundle sync
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        /// <returns></returns>
        public static byte[] LoadBytesSync(string name)
        {
            return ((TextAsset)AssetObtainer.instance.LoadAssetSync(name)).bytes;
        }

        /// <summary>
        /// load texture async
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadTextureAsync(string name, Action<Texture2D> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj)=>
            {
                if (obj != null)
                {
                    if (callback != null) { callback((Texture2D)obj); }
                }
                else
                {
                    ULogger.LogError(string.Format("Load Texture {0} failed !", name));
                }
            });
        }

        /// <summary>
        /// load texture2d sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static Texture2D LoadTextureSync(string name)
        {
            return (Texture2D)AssetObtainer.instance.LoadAssetSync(name);
        }

        /// <summary>
        /// load scriptobject sync
        /// </summary>
        /// <param name="name"></param>
        /// <param name="callback"></param>
        public static void LoadScriptableObjectAsync(string name, Action<ScriptableObject> callback)
        {
            AssetObtainer.instance.LoadAssetAsync(name, (obj)=>
            {
                if(obj != null)
                {
                    if (callback != null) { callback((ScriptableObject)obj); }
                }
                else { ULogger.LogError(string.Format("Load ScriptableObject {0} failed !", name)); }
            });
        }

        /// <summary>
        /// load scriptobject sync
        /// </summary>
        /// <param name="name"></param>
        /// <returns></returns>
        public static ScriptableObject LoadScriptableObjectSync(string name)
        {
            return (ScriptableObject)AssetObtainer.instance.LoadAssetSync(name);
        }

        /// <summary>
        /// load png, tga or jpg file from server sync
        /// </summary>
        /// <param name="url"></param>
        /// <param name="callback"></param>
        public static void LoadImageAsync(string url, Action<Texture2D> callback)
        {
            AssetObtainer.instance.LoadImageAsync(url, callback);
        }

        /// <summary>
        /// delete asset
        /// </summary>
        /// <param name="obj"></param>
        public static void DeleteAsset(UnityEngine.Object obj)
        {
            AssetObtainer.instance.DeleteAsset(obj);
        }

        /// <summary>
        /// clear naming object pool in pool manager
        /// </summary>
        /// <param name="name"></param>
        public static void ClearObjectPool(string name)
        {
            AssetObtainer.instance.ClearObjectPool(name);
        }

        private static void End()
        {
            List<string> list = new List<string>(_name2atlas.Keys);
            for(int i = 0; i < list.Count; i++)
            {
                UnityEngine.Object.Destroy(_name2atlas[list[i]]);
            }
            _name2atlas.Clear();
        }

        public static AssetManager Instance
        {
            get
            {
                if (_instance == null) { _instance = new AssetManager(); }
                return _instance;
            }
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    End();
                    AssetObtainer.instance.Dispose();
                }
                disposedValue = true;
            }
        }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        #endregion
    }
}